#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/spi/spi.h>

#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define REG_DEVICE_ID   0x00
#define REG_DATA_FORMAT 0x31
#define REG_BW_RATE 	0x2C
#define REG_POWER_CTL 	0x2D
#define REG_INT_ENABLE 	0x2E
#define REG_OFSX 		0x1E
#define REG_OFSY 		0x1F
#define REG_OFSZ 		0x20
#define REG_DATAX0 		0x32
#define REG_DATAX1 		0x33
#define REG_DATAY0 		0x34
#define REG_DATAY1 		0x35
#define REG_DATAZ0 		0x36
#define REG_DATAZ1 		0x37

#define ADXL345_MEASURE	0x08

#define ADXL345_CNT				1		  		/* 设备号个数 */
#define ADXL345_NAME			"adxl345_spi"	/* 名字 */

/* newchrled设备结构体 */
struct adxl345_dev{
	dev_t devid;			/* 设备号 	 */
	struct cdev cdev;		/* cdev 	*/
	struct class *class;	/* 类 		*/
	struct device *device;	/* 设备 	 */
	struct device_node *nd; /* 设备节点 */
	int major;				/* 主设备号	  */
	void *private_data; 	/* 私有数据 */
	int cs_gpio; 			/* 片选所使用的 GPIO 编号*/
	unsigned short axisx, axisy, axisz; /* 三个轴的数据 */
};

struct adxl345_dev adxl345dev;	/* 设备 */

/*
 * @description	: 从 adxl345 读取多个寄存器数据
 * @param – dev : adxl345 设备
 * @param – reg : 要读取的寄存器首地址
 * @param – val : 读取到的数据
 * @param – len : 要读取的数据长度
 * @return 		: 操作结果
 */
int adxl345_read_regs(struct adxl345_dev *dev, u8 reg, void *val, int len)
{
	int ret = -1;
	u8 tx_buf[1] = { 0x80 | reg };
	struct spi_device *spi = (struct spi_device *)dev->private_data;
	struct spi_transfer t[2] = {
		{
			.tx_buf = tx_buf,
			.len = 1,
		},
		{
			.rx_buf = val,
			.len = len,
		}
	};

	struct spi_message m;
	spi_message_init(&m); 
    spi_message_add_tail(&t[0], &m);
    spi_message_add_tail(&t[1], &m);

    ret = spi_sync(spi, &m);

    if (ret)
	{
        pr_err("[adxl345] SPI read failed: %d\n", ret);
	}

    return ret;
}

/*
 * @description	: 从 adxl345 写入多个寄存器数据
 * @param – dev : adxl345 设备
 * @param – reg : 要写入的寄存器首地址
 * @param – buf : 要写入的数据缓冲区
 * @param – len : 要写入的数据长度
 * @return 		: 操作结果
 */
s32 adxl345_write_regs(struct adxl345_dev *dev, u8 reg, const u8 *buf, int len)
{
	int ret = -1;
	u8 tx_buf[1] = { (~0x80) & reg };
	struct spi_device *spi = (struct spi_device *)dev->private_data;
	struct spi_transfer t[2] = {
		{
			.tx_buf = tx_buf,
			.len = 1,
		},
		{
			.tx_buf = buf,
			.len = len,
		}
	};

	struct spi_message m;
	spi_message_init(&m); 
    spi_message_add_tail(&t[0], &m);
    spi_message_add_tail(&t[1], &m);

    ret = spi_sync(spi, &m);

    if (ret)
	{
        pr_err("[adxl345] SPI write failed: %d\n", ret);
	}

    return ret;
}

/*
 * @description	: 从 adxl345 读取单个寄存器数据
 * @param – dev : adxl345 设备
 * @param – reg : 要读取的寄存器地址
 * @return 		: 读取到的寄存器值
 */
static u8 adxl345_read_reg(struct adxl345_dev *dev, u8 reg)
{
	u8 data = 0;
	adxl345_read_regs(dev, reg, &data, 1);
	return data;
}

/*
 * @description	: 从 adxl345 写单个寄存器数据
 * @param – dev : adxl345 设备
 * @param – reg : 要读取的寄存器地址
 * @param – data: 要写入的值
 * @return 		: 无
 */
static void adxl345_write_reg(struct adxl345_dev *dev, u8 reg, u8 data)
{
	u8 buf = 0;
	buf = data;
	adxl345_write_regs(dev, reg, &buf, 1);
}

/*
 * @description	: 从 adxl345 写单个寄存器数据并检查
 * @param – dev : adxl345 设备
 * @param – reg : 要读取的寄存器地址
 * @param – data: 要写入的值
 * @return 		: 无
 */
static void adxl345_write_reg_with_check(struct adxl345_dev *dev, u8 reg, u8 data)
{
	adxl345_write_reg(dev, reg, data);
	if (adxl345_read_reg(dev, reg) != data)
	{
		printk("[adxl345] write_reg_with_check failed!\r\n");
	}
}

/*
 * @description	: 从 adxl345 读取三轴数据
 * @param – dev : adxl345 设备
 * @return 		: 无
 */
void adxl345_readdata(struct adxl345_dev *dev)
{
	u8 i = 0;
	u8 buf[6];
	
	for (i = 0; i < 6; i++)
	{
		buf[i] = adxl345_read_reg(dev, REG_DATAX0 + i);
	}

	dev->axisx = ((unsigned short)buf[1] << 8) | buf[0];
	dev->axisy = ((unsigned short)buf[3] << 8) | buf[2];
	dev->axisz = ((unsigned short)buf[5] << 8) | buf[4];
}

/*
 * @description	: adxl345 内部寄存器初始化
 * @param		: 无
 * @return 		: 无
 */
void adxl345_reqinit(void)
{
	/* 初始化adxl345 */
	u8 device_id = adxl345_read_reg(&adxl345dev, REG_DEVICE_ID);
	adxl345_write_reg_with_check(&adxl345dev, REG_DATA_FORMAT, 0x0b);  
	adxl345_write_reg_with_check(&adxl345dev, REG_BW_RATE, 0x0b);
	adxl345_write_reg_with_check(&adxl345dev, REG_POWER_CTL, 0x08);
	adxl345_write_reg_with_check(&adxl345dev, REG_INT_ENABLE, 0x80);
	adxl345_write_reg_with_check(&adxl345dev, REG_OFSX, 0x00);
	adxl345_write_reg_with_check(&adxl345dev, REG_OFSY, 0x00);
	adxl345_write_reg_with_check(&adxl345dev, REG_OFSZ, 0x05);

	printk("[adxl345] device_id = 0x%x!\r\n", device_id);
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件，file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int adxl345_open(struct inode *inode, struct file *filp)
{
	printk("[adxl345] open!\r\n");
	filp->private_data = &adxl345dev; /* 设置私有数据 */

	return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数，如果为负值，表示读取失败
 */
static ssize_t adxl345_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	unsigned short data[3];
	long err = 0;

	struct adxl345_dev *dev = filp->private_data;
	
	adxl345_readdata(dev);

	data[0] = dev->axisx;
	data[1] = dev->axisy;
	data[2] = dev->axisz;

	err = copy_to_user(buf, data, sizeof(data));
	return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int adxl345_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/* 设备操作函数 */
static struct file_operations adxl345_fops = {
	.owner = THIS_MODULE,
	.open = adxl345_open,
	.read = adxl345_read,
	.release = 	adxl345_release,
};

/*
 * @description		: flatform 驱动的 probe 函数，当驱动与设备匹配以后此函数就会执行
 * @param - spi 	: spi 设备
 * @return 			: 0，成功;其他负值,失败
 */
static int adxl345_probe(struct spi_device *spi)
{
	printk("adxl345_spi driver and device has matched!\r\n");

	/* 注册字符设备驱动 */
	/* 1、创建设备号 */
	alloc_chrdev_region(&adxl345dev.devid, 0, ADXL345_CNT, ADXL345_NAME);	/* 申请设备号 */
	adxl345dev.major = MAJOR(adxl345dev.devid);	/* 获取分配号的主设备号 */
	printk("[adxl345] major=%d \r\n",adxl345dev.major);	
	
	/* 2,3 是创建一个新的字符设备 */
	/* 2、初始化cdev */
	adxl345dev.cdev.owner = THIS_MODULE;
	cdev_init(&adxl345dev.cdev, &adxl345_fops);
	
	/* 3、添加一个cdev */
	cdev_add(&adxl345dev.cdev, adxl345dev.devid, ADXL345_CNT);

	/* 4,5 是为了能够自动试别设备，也就是热插拔 */
	/* 4、创建类 */
	adxl345dev.class = class_create(THIS_MODULE, ADXL345_NAME);
	if (IS_ERR(adxl345dev.class)) {
		return PTR_ERR(adxl345dev.class);
	}

	/* 5、创建设备 */
	adxl345dev.device = device_create(adxl345dev.class, NULL, adxl345dev.devid, NULL, ADXL345_NAME);
	if (IS_ERR(adxl345dev.device)) {
		return PTR_ERR(adxl345dev.device);
	}
	
	/*初始化 spi_device */
	spi->mode = SPI_MODE_3; /*MODE3，CPOL=1，CPHA=1*/
	spi_setup(spi);
	adxl345dev.private_data = spi;

	/* 初始化 adxl345 内部寄存器 */
	adxl345_reqinit();

	return 0;
}

/*
 * @description		: 移除 platform 驱动的时候此函数会执行
 * @param - spi 	: spi 设备
 * @return 			: 0，成功;其他负值,失败
 */
static int adxl345_remove(struct spi_device *spi)
{
	/* 注销字符设备驱动 */
	cdev_del(&adxl345dev.cdev);/*  删除cdev */
	unregister_chrdev_region(adxl345dev.devid, ADXL345_CNT); /* 注销设备号 */

	device_destroy(adxl345dev.class, adxl345dev.devid);
	class_destroy(adxl345dev.class);
	return 0;
}

/* 传统匹配方式 ID 列表 */ 
static const struct spi_device_id adxl345_id[] = {
	{"alientek,adxl345_spi", 0},
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id adxl345_of_match[] = {
	{ .compatible = "alientek,adxl345_spi" },
	{ /* Sentinel */ }
};

/* spi 驱动结构体 */
static struct spi_driver adxl345_driver = {
	.probe = adxl345_probe,
	.remove = adxl345_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "adxl345_spi", /* 驱动名字，用于和设备匹配 */
		.of_match_table = adxl345_of_match, /* 设备树匹配表 */
	},
	.id_table = adxl345_id,
};

/*
 * @description	: 驱动模块加载函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init adxl345_init(void)
{
	return spi_register_driver(&adxl345_driver);
}

/*
 * @description	: 驱动模块卸载函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit adxl345_exit(void)
{
	spi_unregister_driver(&adxl345_driver);
}

module_init(adxl345_init);
module_exit(adxl345_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("huangyu");
